﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.MathLib
{

    public class Prism3d : Solid3d
    {
        //private double topWidth;
        //private double bottomWidth;
        private double height;
        private int side;
        private bool openEnded;
        //private Vector3 normal= new Vector3(0, 0, 1);
        private bool isInnerCircle = false;
        private double topRadius;
        private double bottomRadius;
        //public double TopWidth { get => topWidth; set => SetSize(side, value, bottomWidth, height,  openEnded, normal); }
        //public double BottomWidth { get => bottomWidth; set => SetSize(side, topWidth, value,  height, openEnded, normal); }
        //public Vector3 Normal { get => this.normal; set => SetSize(side, topRadius, bottomRadius, height, openEnded, value); }
        public int Side { get => side; set => SetSize(value, topRadius, bottomRadius, height, isInnerCircle,openEnded); }
        public double TopRadius { get => topRadius; set => SetSize(side, value, bottomRadius, height, isInnerCircle, openEnded); }
        public double BottomRadius { get => bottomRadius; set => SetSize(side, topRadius, value, height, isInnerCircle, openEnded); }
        public double Height { get => height; set => SetSize(side, topRadius, bottomRadius, value, isInnerCircle, openEnded); }
        public bool IsInnerCircle { get => isInnerCircle; set => SetSize(side, topRadius, bottomRadius, height, value, openEnded); }
        public bool OpenEnded { get => openEnded; set => SetSize(side, topRadius, bottomRadius, height, isInnerCircle, value); }

        //public Prism3d(int side = 3 , double topWidth = 1, double bottomWidth = 1, double height = 1,bool openEnded = false)
        //{
        //    this.SetSize(side, topWidth, bottomWidth, height, openEnded,this.normal);
        //}
        public Prism3d(int side = 3, double topRadius = 10, double bottomRadius = 10, double height = 10, bool openEnded = false,bool isInnerCircle = false)
        {
            this.SetSize(side, topRadius, bottomRadius, height, openEnded, this.isInnerCircle);
        }
        public Surface3d[] Surfaces;
        private TopoFaceModel topoFaceModel;
        //public Prism3d SetSize(int side, double topWidth, double bottomWidth, double height, bool openEnded,Vector3 normal)
        //{
        //    if (topoFaceModel != null && (this.side != side || this.topWidth != topWidth || this.bottomWidth != bottomWidth || this.height != height  ||  this.openEnded != openEnded||   this.normal != normal))
        //    {
        //        topoFaceModel = null;
        //    }
        //    this.side = side<3?3:side;
        //    this.topWidth = topWidth;
        //    this.bottomWidth = bottomWidth;
        //    this.topRadius = TopWidth / 2 / Math.Sin(Math.PI / this.Side);
        //    this.bottomRadius = bottomWidth / 2 / Math.Sin(Math.PI / this.Side);
        //    this.bottomRadius=
        //    this.height = height ;  
        //    this.openEnded = openEnded ; 
        //    this.normal = normal ;
        //    return this;
        //}
        public Prism3d SetSize(int side, double topRadius, double bottomRadius, double height, bool isInnerCircle, bool openEnded)
        {
            if (topoFaceModel != null && (this.side != side || this.topRadius != topRadius || this.bottomRadius != bottomRadius || this.height != height || this.openEnded != openEnded || this.isInnerCircle != isInnerCircle))
            {
                topoFaceModel = null;
            }
            this.side = side < 3 ? 3 : side;
            this.topRadius = topRadius;
            this.bottomRadius = bottomRadius;
            //this.topWidth = this.topRadius * 2 * Math.Sin(Math.PI / this.Side);
            //this.bottomWidth = this.topRadius * 2 * Math.Sin(Math.PI / this.Side);
            this.height = height;
            this.openEnded = openEnded;
            this.isInnerCircle = isInnerCircle;
            return this;
        }
        private List<Vector3> topVertexs;
        private List<Vector3> bottomVertexs;
        public override TopoFaceModel CreateTopoModel()
        {
            if (topoFaceModel != null)
                return topoFaceModel;
            //var origin = new Vector3();
            //var angle = this.Normal.Normalize().AngleTo(new Vector3(0,0,1));
            //var offset = this.height / Math.Cos(angle);
            //var topOrigin = origin.Clone().AddScaledVector(this.Normal.Clone().Normalize(), offset);
            topVertexs = new List<Vector3>();
            bottomVertexs = new List<Vector3>();
            if (isInnerCircle)
            {
                topVertexs = new Arc3d(new Vector3(0, 0, height), topRadius, 0, Utils.TwoPI).GetPoints(this.Side);
                bottomVertexs = new Arc3d(new Vector3(), bottomRadius, 0, Utils.TwoPI).GetPoints(this.Side);
            }
            else
            {
                topVertexs = new Arc3d(new Vector3(0, 0, height),topRadius/ Math.Cos(Math.PI / this.Side), 0, Utils.TwoPI).GetPoints(this.Side);
                bottomVertexs = new Arc3d(new Vector3(),   bottomRadius/ Math.Cos(Math.PI / this.Side), 0, Utils.TwoPI).GetPoints(this.Side);
            }
            var topCurve = new List<Curve3d>();
            var bottomCurve = new List<Curve3d>();
            var faces = new List<Surface3d>();
            for (int i=0;i<side;i++)
            {
                topCurve.Add(new Line3d(topVertexs[i].Clone(), topVertexs[i + 1].Clone()));
                bottomCurve.Add(new Line3d(bottomVertexs[i].Clone(), bottomVertexs[i + 1].Clone()));
                var sideCurve=new List<Curve3d>();
                sideCurve.Add(new Line3d(bottomVertexs[i].Clone(), bottomVertexs[i + 1].Clone()));
                sideCurve.Add(new Line3d(bottomVertexs[i + 1].Clone(), topVertexs[i+1].Clone()));
                sideCurve.Add(new Line3d(topVertexs[i + 1].Clone(), topVertexs[i].Clone()));
                sideCurve.Add(new Line3d(topVertexs[i].Clone(), bottomVertexs[i].Clone()));
                var xAxis= topVertexs[i + 1].Clone().Sub(topVertexs[i]);
                var sideNormal = new Vector3(0, 0, 1).Cross(xAxis).Normalize().Negate();
                var sideFace = new PlanarSurface3d(new Plane(sideNormal), sideCurve);
                faces.Add(sideFace);
            }
            if (!openEnded)
            {
                var topFace = new PlanarSurface3d(new Plane(new Vector3(0, 0, 1)), topCurve);
                var bottomFace = new PlanarSurface3d(new Plane(new Vector3(0, 0, -1)), bottomCurve);
                faces.Add(topFace);
                faces.Add(bottomFace);
            }
            this.Surfaces = faces.ToArray();
            this.topoFaceModel = new TopoFaceModel() { Surfaces = this.Surfaces.ToListEx() };
            return this.topoFaceModel;
        }
        public override Solid3d CreateMesh()
        {
            base.CreateMesh();

            var idxsCount = this.Geometry.Indics.Length;
            if (!openEnded)
            {
                this.Geometry.Groups[Side].MaterialIndex = 1;
                this.Geometry.Groups[Side + 1].MaterialIndex = 2;
            }
            this.Edge = new GeometryData();
            var verteics = new List<Vector3>();
            var idxArray = new ListEx<int>() {  };
            if (topVertexs!=null&&bottomVertexs!=null)
            {
                for (var i = 0; i < side; i++)
                {
                    var count = verteics.Count;
                    verteics.Add(topVertexs[i]);
                    verteics.Add(bottomVertexs[i]);
                    idxArray.Push(count, count + 1);
                    idxArray.Push(count, count + 2);
                    idxArray.Push(count + 1, count + 3);
                }
                verteics.Add(topVertexs[side]);
                verteics.Add(bottomVertexs[side]);
                this.Edge.Verteics = Utils.GenerateVertexArr(verteics);
                this.Edge.Indics = idxArray.ToArray();
            }
            return this;
        }
    }
}
